
#include "Thread.h"

BEGIN_GLSX_NAMESPACE

#define _CONDITION_EVENT_ONE 0
#define _CONDITION_EVENT_ALL 1


	ConditionVariable::ConditionVariable() : mWaitersCount(0)
	{
		mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
		mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
		InitializeCriticalSection(&mWaitersCountLock);
	}


	ConditionVariable::~ConditionVariable()
	{
		CloseHandle(mEvents[_CONDITION_EVENT_ONE]);
		CloseHandle(mEvents[_CONDITION_EVENT_ALL]);
		DeleteCriticalSection(&mWaitersCountLock);
	}

	void ConditionVariable::_wait()
	{
		// Wait for either event to become signaled due to notifyOne() or
		// notifyAll() being called
		int result = WaitForMultipleObjects(2, mEvents, FALSE, INFINITE);

		// Check if we are the last waiter
		EnterCriticalSection(&mWaitersCountLock);
		-- mWaitersCount;
		bool lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) &&
			(mWaitersCount == 0);
		LeaveCriticalSection(&mWaitersCountLock);

		// If we are the last waiter to be notified to stop waiting, reset the event
		if(lastWaiter) ResetEvent(mEvents[_CONDITION_EVENT_ALL]);
	}

	void ConditionVariable::notifyOne()
	{
		EnterCriticalSection(&mWaitersCountLock);
		bool haveWaiters = (mWaitersCount > 0);
		LeaveCriticalSection(&mWaitersCountLock);

		if(haveWaiters) SetEvent(mEvents[_CONDITION_EVENT_ONE]);
	}


	void ConditionVariable::notifyAll()
	{
		// Are there any waiters?
		EnterCriticalSection(&mWaitersCountLock);
		bool haveWaiters = (mWaitersCount > 0);
		LeaveCriticalSection(&mWaitersCountLock);

		// If we have any waiting threads, send them a signal
		if(haveWaiters) SetEvent(mEvents[_CONDITION_EVENT_ALL]);
	}

	struct _thread_start_info 
	{
		void (*mFunction)(void *); ///< Pointer to the function to be executed.
		void * mArg;               ///< Function argument for the Thread function.
		Thread * mThread;          ///< Pointer to the Thread object.
	};

	DWORD WINAPI Thread::wrapperFunction(LPVOID * aArg)
	{
		// Get Thread startup information
		_thread_start_info * ti = (_thread_start_info *) aArg;

		try
		{
			// Call the actual client Thread function
			ti->mFunction(ti->mArg);
		}
		catch(...)
		{
		}

		// The Thread is no longer executing
		LockGuard<Mutex> guard(ti->mThread->mDataMutex);
		ti->mThread->mNotAThread = true;
		delete ti;
		return 0;
	}

	Thread::Thread(void (*aFunction)(void *), void * aArg)
	{
		LockGuard<Mutex> guard(mDataMutex);

		_thread_start_info * ti = new _thread_start_info;
		ti->mFunction = aFunction;
		ti->mArg = aArg;
		ti->mThread = this;

		// The Thread is now alive
		mNotAThread = false;

		mHandle = (HANDLE) CreateThread(0, 0, (LPTHREAD_START_ROUTINE)wrapperFunction, (void *)ti, 0, &mWin32ThreadID);

		if(!mHandle)
		{
			mNotAThread = true;
			delete ti;
		}
	}

	Thread::~Thread()
	{
	}

	void Thread::join()
	{
		if(joinable())
		{
			WaitForSingleObject(mHandle, INFINITE);
			CloseHandle(mHandle);
		}
	}

	bool Thread::joinable() const
	{
		mDataMutex.lock();
		bool result = !mNotAThread;
		mDataMutex.unlock();
		return result;
	}

	void Thread::detach()
	{
		mDataMutex.lock();
		if(!mNotAThread)
		{
			CloseHandle(mHandle);
			mNotAThread = true;
		}
		mDataMutex.unlock();
	}

	DWORD Thread::getId() const
	{
		return mWin32ThreadID;
	}

	void Thread::yield()
	{
		sleep(0);
	}

	void Thread::sleep( unsigned int ms )
	{
		::Sleep(ms);
	}




END_GLSX_NAMESPACE
